﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using Common.Logging;
using Crawler.Configuration;
using Crawler.Data;
using Crawler.Reports;
using Crawler.Stuff;
using Newtonsoft.Json;

namespace Crawler.VcontakteAPI
{
    public class VcontacteContext : IDisposable
    {
        private readonly string email;
        private readonly string pwd;

        public const int AppsPerPage = 20;
        private const string PostDataFormat = "c[section]=apps&c[sort]=0&offset={0}&preload=1";
        private const string PostDataFormat1 = "c[section]=apps&c[sort]=1&c[type]={0}&offset={1}&preload=1";
        private const string PostDataFormat0Section = "c[section]=apps&c[sort]=1";
        private const string PostDataFormatAppender = "c[type]={0}";

        private const int TypeCount = 6;
        private const string AppsUrl = "http://vkontakte.ru/gsearch.php?from=apps&ajax=1";
        private readonly int _maxAttempts = CrawlerSection.Settings.LoginInfo.Count;
        private int attempt = 0;

        private Request r;
        private Parser parser;
        public ReportStore _reportStore;

        private ILog log;

        public string AuthCookie
        {
            get;
            private set;
        }

        public VcontacteContext()
        {
            log = LogManager.GetCurrentClassLogger();
            log.Debug("VcontacteContext constracted");
         }

        public void Init()
        {
            _reportStore = ReportStore.New("");
            parser = new Parser(_reportStore);
        }

        public VcontacteContext(string email, string pwd)
            : this()
        {
            this.email = email;
            this.pwd = pwd;
        }

        public void SignIn()
        {
            SignIn(CrawlerSection.Settings.LoginInfo[0].Email, CrawlerSection.Settings.LoginInfo[0].Pwd);
        }

        public void SignIn(string email, string pwd)
        {
            var r = new Request
                                       {
                                           Url = "http://login.vk.com/?act=login",
                                           Referrer = "http://vkontakte.ru/index.php",
                                           PostData =
                                               string.Format("email={0}&pass={1}&expire=&vk=", HttpUtility.UrlEncode(email),
                                                             HttpUtility.UrlEncode(pwd)),
                                           Method = "POST",
                                           KeepAlive = true,
                                           ContentType = "application/x-www-form-urlencoded"
                                       };

            HttpWebResponse response;
            if (r.TryGetResponse(out response))
            {
                var html = r.GetHtml(response);
                string postData = Parser.FromInputToPostData(html);

                var r2 = new Request
                             {
                                 Url = "http://vkontakte.ru/login.php",
                                 Referrer = "http://login.vk.com/?act=login",
                                 PostData = postData,
                                 Method = "POST",
                                 Cookie = "remixchk=5; remixsid=nonenone",
                                 KeepAlive = true,
                                 ContentType = "application/x-www-form-urlencoded"
                             };
                HttpWebResponse response2;
                if (r2.TryGetResponse(out response2))
                {
                    AuthCookie = response2.Headers["Set-Cookie"] + ";remixchk=5";
                    LogManager.GetCurrentClassLogger().DebugFormat("Cookie sets to {0}", AuthCookie);
                }
            }
        }

        public void Parse()
        {
            if (AuthCookie == null)
            {
                throw new ArgumentNullException("authCookie");
            }
            Init();
            ExecuteParsing();
        }

        private void NextCrendetails()
        {
            SignIn(CrawlerSection.Settings.LoginInfo[attempt].Email, CrawlerSection.Settings.LoginInfo[attempt].Pwd);
            r.Cookie = AuthCookie;
        }

        private void ExecuteParsing()
        {
            PrepareToParsing();
            for (int f = 0; f < TypeCount; f++)
            {
                log.DebugFormat("Enter section {0}", f);
                r.PostData = f == 0 ? PostDataFormat0Section : PostDataFormat0Section + string.Format(PostDataFormatAppender, f); //f =0; i =0
                r.i = 0;
                r.f = f;

                string json = GetJson(r);
                VkontakteJson data = GetData(f, 0, json);
                int count = 1000;
                if (data == null)
                {
                    StoreFail(r);
                }
                else
                {
                    count = parser.AppsCount(Parser.LoadHtml(data.Summary));
                    if (count != 0)
                    {
                        Parse(f, 0, data);
                    }
                }
                int iterations = count >= 1000 ? 1000 / AppsPerPage : (int)Math.Ceiling((double)count / AppsPerPage);
                for (int i = 1; i < iterations; i++)
                {
                    log.DebugFormat("Enter sub-section {0}, section {1}", i, f);
                    r.i = i;
                    r.f = f;
                    if (f == 0)
                    {
                        r.PostData = string.Format(PostDataFormat, i * 20);
                    }
                    else
                    {
                        r.PostData = string.Format(PostDataFormat1, f, i * 20);
                    }
                    string json2 = GetJson(r);
                    VkontakteJson data2 = GetData(f, i, json2);
                    if (data2 == null)
                    {
                        StoreFail(r);
                        continue;
                    }
                    Parse(f, i, data2);
                }
            }
            _reportStore.Finish();
        }

        public void RepairRequests(IEnumerable<Request> requests)
        {
            Init();
            _reportStore.Report.Summary += "Repair";
            _reportStore.UpdateProperties();
            log.DebugFormat("Attempt to repair {0} failed requests", requests.Count());
            int n = 0;
            foreach (var request in requests)
            {
                request.Refresh();
                request.Cookie = AuthCookie;
                log.DebugFormat("Attempt to repair {0} failed requests, post data {1}", n, request.PostData);
                var json = GetJson(request);
                var data = GetData(request.f, request.i, json);
                if (data == null)
                {
                    StoreFail(request);
                    continue;
                }
                Parse(request.f, request.i, data);
                n++;
            }
            _reportStore.Finish();
        }

        private string GetJson(Request request)
        {
            string json;
            while (!request.TryGetHtml(out json) && attempt < _maxAttempts)
            {
                SignIn(CrawlerSection.Settings.LoginInfo[attempt].Email, CrawlerSection.Settings.LoginInfo[attempt].Pwd);
                attempt++;
                request.Cookie = AuthCookie;
            }
            attempt = 0;
            return json;
        }

        private void StoreFail(Request request)
        {
            _reportStore.AddFailedRequest(request);
            _reportStore.Report.UrlsSkipped += 1;
            _reportStore.UpdateProperties();
            log.FatalFormat("Max attempts achieved. Skip request attempts. PostData: {0}", request.PostData);
        }

        private void PrepareToParsing()
        {
            _reportStore.Report.Summary += "Parsing";
            _reportStore.UpdateProperties();
            log.DebugFormat("ParsingService ExecuteParsing. coockie: {0}", AuthCookie);
            log.Debug("ParsingService ExecuteParsing");
            r = new Request
                    {
                        Method = "POST",
                        Cookie = AuthCookie,
                        Url = AppsUrl
                    };

        }

        private void Parse(int f, int i, VkontakteJson data)
        {

            if (f == 0) // секция с рейтингом
            {
                parser.ParseAndStore(Parser.LoadHtml(data.Rows), i);
            }
            else
            {
                parser.ParseAndStore(Parser.LoadHtml(data.Rows));
            }
        }

        private VkontakteJson GetData(int f, int i, string json2)
        {
            VkontakteJson data2 = null;
            try
            {
                data2 = JsonConvert.DeserializeObject<VkontakteJson>(json2);
            }
            catch (Exception e)
            {
                log.FatalFormat("section {0},{1}", f, i);
                log.FatalFormat("Can't convert data. Json: {0}, Message: {1}, Type: {2}, StackTrace: {3}", json2, e.Message, e.GetType(), e.StackTrace);
            }
            return data2;
        }

      



        #region IDisposable Members

        public void Dispose()
        {

        }

        #endregion
    }
}
